/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Projects
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <DateTime.hpp>

#include <sstream>

///@brief Default constructor
DateTime::DateTime()
{
    year = 1970;
    month = 1;
    day = 1;
    hour = 0;
    minute = 0;
    second = 0;
}

///@brief Constructor with arguments
///@param Dyear Year value
///@param Dmonth Month value
///@param Dday Day value
///@param Dhour	Hour value
///@param Dminute Minute value
///@param Dsecond Second value
DateTime::DateTime(unsigned int Dyear, unsigned int Dmonth, unsigned int Dday,
                   unsigned int Dhour, unsigned int Dminute, unsigned int Dsecond)
{
    year = Dyear;
    month = Dmonth;
    day = Dday;
    hour = Dhour;
    minute = Dminute;
    second = Dsecond;
}

///@brief Copy constructor
///@param d DateTime object to copy
DateTime::DateTime(const DateTime& d)
{
    year = d.year;
    month = d.month;
    day = d.day;
    hour = d.hour;
    minute = d.minute;
    second = d.second;
}

///@brief Destructor
DateTime::~DateTime()
{

}

///@brief Overload of = operator
///@param d DateTime object as right hand operand
DateTime& DateTime::operator=(const DateTime &d)
{
    year = d.year;
    month = d.month;
    day = d.day;
    hour = d.hour;
    minute = d.minute;
    second = d.second;
    return (*this);
}

///@brief Return the date and time as a string with given format
///@param f return format
std::string DateTime::AsString(std::string f)
{
    std::string res = f;
    size_t found;
    //Recherche de l'année en 4 caractères
    found = f.find("yyyy");
    if(found == std::string::npos)
    {
        //Recherche de l'année en 2 caractères
        found = f.find("yy");
        if(found != std::string::npos)
        {
            std::string yearString2;
            std::ostringstream streamYear2;
            streamYear2 << year % 100;
            yearString2 = streamYear2.str();
            while(yearString2.length() != 2)
                yearString2 = "0"+yearString2;
            res.replace(found, 2, yearString2);
        }
    }
    else
    {
        std::string yearString4;
        std::ostringstream streamYear4;
        streamYear4 << year;
        yearString4 = streamYear4.str();
        while(yearString4.length() != 4)
            yearString4 = "0"+yearString4;
        res.replace(found, 4, yearString4);
    }

    //Recherche du mois en 2 caractères
    found = f.find("mm");
    if(found != std::string::npos)
    {
        std::string monthString2;
        std::ostringstream streamMonth2;
        streamMonth2 << month;
        monthString2 = streamMonth2.str();
        while(monthString2.length() != 2)
            monthString2 = "0"+monthString2;
        res.replace(found, 2, monthString2);
    }
    
    //Recherche du jour en 2 caractères
    found = f.find("dd");
    if(found != std::string::npos)
    {
        std::string dayString2;
        std::ostringstream streamDay2;
        streamDay2 << day;
        dayString2 = streamDay2.str();
        while(dayString2.length() != 2)
            dayString2 = "0"+dayString2;
        res.replace(found, 2, dayString2);
    }

    //Recherche de l'heure en 2 caractères
    found = f.find("hh");
    if(found != std::string::npos)
    {
        std::string hourString2;
        std::ostringstream streamHour2;
        streamHour2 << hour;
        hourString2 = streamHour2.str();
        while(hourString2.length() != 2)
            hourString2 = "0"+hourString2;
        res.replace(found, 2, hourString2);
    }
    
    //Recherche des minutes en 2 caractères
    found = f.find("nn");
    if(found != std::string::npos)
    {
        std::string minuteString2;
        std::ostringstream streamMinute2;
        streamMinute2 << minute;
        minuteString2 = streamMinute2.str();
        while(minuteString2.length() != 2)
            minuteString2 = "0"+minuteString2;
        res.replace(found, 2, minuteString2);
    }
    
    //Recherche des secondes en 2 caractères
    found = f.find("ss");
    if(found != std::string::npos)
    {
        std::string secondString2;
        std::ostringstream streamSecond2;
        streamSecond2 << second;
        secondString2 = streamSecond2.str();
        while(secondString2.length() != 2)
            secondString2 = "0"+secondString2;
        res.replace(found, 2, secondString2);
    }
    
    return res;
}

///@brief Set the DateTime object from a string with given format
///@param t String containing the DateTime value
///@param f String containing the format
void DateTime::SetWithFormat(std::string t, std::string f)
{
    if(t.length() != f.length())
        return;
    size_t found;
    //Looking for "yyyy" string
    found = f.find("yyyy");
    if(found != std::string::npos)
    {
        std::string yearString = t.substr((int)found, 4);
        std::istringstream stream(yearString);
        stream >> year;
    }

    //Looking for "mm" string
    found = f.find("mm");
    if(found != std::string::npos)
    {
        std::string monthString = t.substr((int)found, 2);
        std::istringstream stream(monthString);
    stream >> month;
    }

    //Looking for "dd" string
    found = f.find("dd");
    if(found != std::string::npos)
    {
        std::string dayString = t.substr((int)found, 2);
        std::istringstream stream(dayString);
        stream >> day;
    }

    //Looking for "hh" string
    found = f.find("hh");
    if(found != std::string::npos)
    {
        std::string hourString = t.substr((int)found, 2);
        std::istringstream stream(hourString);
        stream >> hour;
    }

    //Looking for "nn" string
    found = f.find("nn");
    if(found != std::string::npos)
    {
        std::string minuteString = t.substr((int)found, 2);
        std::istringstream stream(minuteString);
        stream >> minute;
    }
    
    //Looking for "ss" string
    found = f.find("ss");
    if(found != std::string::npos)
    {
        std::string secondString = t.substr((int)found, 2);
        std::istringstream stream(secondString);
        stream >> second;
    }
}

///@brief Return number of days from an initial date
unsigned int DateTime::NumberOfDays()
{
    unsigned int b = 365*year;
    unsigned int c = 31*(month - 1);
    unsigned int d, e, h;
    if((month == 1)||(month == 2))
    {
        d = 0;
        e = (int)((year-1)/4.);
        h = (int)(0.75*((int)((year - 1)/100.) + 1.));
    }
    else
    {
        d = (int)(0.4 * month + 2.3);
        e = (int)(year/ 4.);
        h = (int)(0.75*((int)(year / 100.)+1.));
    }
    return day+b+c-d+e-h;
}

///@brief Return true if current year is bissextile
///@return True if current year is bissextile, false otherwise
bool DateTime::IsBissextile()
{
    if(((year % 4 == 0)&&(year % 100 != 0))||(year % 400 == 0))
        return true;
    return false;
}

///@brief Return the number of days in the current month
unsigned int DateTime::NumberOfDaysInTheMonth()
{
    switch(month)
    {
        case 1:
            return 31;
        case 2:
        {
            if(IsBissextile())
                return 29;
            else
                return 28;
        }
        case 3:
            return 31;
        case 4:
            return 30;
        case 5:
            return 31;
        case 6:
            return 30;
        case 7:
            return 31;
        case 8:
            return 31;
        case 9:
            return 30;
        case 10:
            return 31;
        case 11:
            return 30;
        case 12:
            return 31;
    }
}

///@brief Normalize the contained values
///@warning the normalization only works for hour/minute/second adjustment
void DateTime::Normalize()
{
    second = second + 60*minute + 3600*hour;
    hour = second / 3600;
    minute = (second % 3600) / 60;
    second = (second % 3600) % 60;

    if(hour >= 24)
    {
        hour = hour - 24;
        day ++;
    }

    if(day > NumberOfDaysInTheMonth())
    {
        day = day - NumberOfDaysInTheMonth();
        month ++;
    }

    if(month > 12)
    {
        month = month - 12;
        year ++;
    }
}


///@brief Return hour value
unsigned int DateTime::GetHour()
{
    return hour;
}

///@brief Return minute value
unsigned int DateTime::GetMinute()
{
    return minute;
}

///@brief Return second value
unsigned int DateTime::GetSecond()
{
    return second;
}

///@brief Return year value
unsigned int DateTime::GetYear()
{
    return year;
}

///@brief Return month value
unsigned int DateTime::GetMonth()
{
    return month;
}

///@brief Return day value
unsigned int DateTime::GetDay()
{
    return day;
}

///@brief Return index of day in the week. Monday = 1
unsigned int DateTime::GetDayOfWeek()
{
    //1st January 1900 is a Monday
    DateTime refDate;
    refDate.SetWithFormat("01/01/1900", "dd/mm/yyyy");
    
    unsigned int nDays = NumberOfDays() - refDate.NumberOfDays();
    return (nDays % 7)+1;
}

///@brief Set year value
///@param Dyear year value to be set
void DateTime::SetYear(unsigned int Dyear)
{
    year = Dyear;
}

///@brief Set month value
///@param Dmonth month value to be set
void DateTime::SetMonth(unsigned int Dmonth)
{
    month = Dmonth;
}

///@brief Set day value
///@param Dday day value to be set
void DateTime::SetDay(unsigned int Dday)
{
    day = Dday;
}

///@brief Set hour value
///@param Dhour hour value to be set
void DateTime::SetHour(unsigned int Dhour)
{
    hour = Dhour;
}

///@brief Set minute value
///@param Dminute minute value to be set
void DateTime::SetMinute(unsigned int Dminute)
{
    minute = Dminute;
}

///@brief Set second value
///@param Dsecond second value to be set
void DateTime::SetSecond(unsigned int Dsecond)
{
    second = Dsecond;
}

///@brief Overload of << operator
///@param s Out stream
///@param d DateTime object to put in the out stream
std::ostream& operator<<(std::ostream& s, const DateTime& d)
{
    if(d.year < 1000)
        s << "0" ;
    if(d.year < 100)
        s << "0";
    if(d.year < 10)
        s << "0";
    s << d.year << "-";
    if(d.month < 10)
        s << "0";
    s << d.month << "-";
    if(d.day < 10)
        s << "0";
    s << d.day << "--";
    if(d.hour < 10)
        s << "0";
    s << d.hour << ":";
    if(d.minute < 10)
        s << "0";
    s << d.minute << ":";
    if(d.second < 10)
        s << "0";
    s << d.second;
    return s;
}

///@brief Overload of == operator : comparison of 2 DateTime objects
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator==(const DateTime& d1, const DateTime& d2)
{
    if(d1.hour != d2.hour)
        return false;
    if(d1.minute != d2.minute)
        return false;
    if(d1.second != d2.second)
        return false;
    if(d1.year != d2.year)
        return false;
    if(d1.month != d2.month)
        return false;
    if(d1.day !=  d2.day)
        return false;
    return true;
}

///@brief Overload of != operator : comparison of 2 DateTime objects
///@brief The comparison doesn't take into account the seconds value
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator!=(const DateTime& d1, const DateTime& d2)
{
    return !(d1 == d2);
}


///@brief Overload of < operator : comparison of 2 DateTime objects
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator<(const DateTime& d1, const DateTime& d2)
{
    if(d1.year < d2.year)
        return true;
    else if(d1.year > d2.year)
        return false;

    //From here d1.year and d2.year should be equal
    if(d1.month < d2.month)
        return true;
    else if(d1.month > d2.month)
        return false;

    //From here, d1.month and d2.month should be equal
    if(d1.day < d2.day)
        return true;
    else if(d1.day > d2.day)
        return false;

    //From here, d1.day and d2.day should be equal
    if(d1.hour < d2.hour)
        return true;
    else if(d1.hour > d2.hour)
        return false;

    //From here, d1.hour and d2.hour should be equal
    if(d1.minute < d2.minute)
        return true;
    else if(d1.minute > d2.minute)
        return false;

    //From here, d1.minute and d2.minute should be equal
    if(d1.second < d2.second)
        return true;
    else if(d1.second > d2.second)
        return false;

    //From here, d1.second and d2.second should be equal
    return false;
}

///@brief Overload of > operator : comparison of 2 DateTime objects
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator>(const DateTime& d1, const DateTime& d2)
{

    if(d1.year > d2.year)
        return true;
    else if(d1.year < d2.year)
        return false;

    //From here d1.year and d2.year should be equal
    if(d1.month > d2.month)
        return true;
    else if(d1.month < d2.month)
        return false;

    //From here, d1.month and d2.month should be equal
    if(d1.day > d2.day)
        return true;
    else if(d1.day < d2.day)
        return false;

    //From here, d1.day and d2.day should be equal
    if(d1.hour > d2.hour)
        return true;
    else if(d1.hour < d2.hour)
        return false;

    //From here, d1.hour and d2.hour should be equal
    if(d1.minute > d2.minute)
        return true;
    else if(d1.minute < d2.minute)
        return false;

    //From here, d1.minute and d2.minute should be equal
    if(d1.second > d2.second)
        return true;
    else if(d1.second < d2.second)
        return false;

    //From here, d1.second and d2.second should be equal
    return false;
}

///@brief Overload of <= operator : comparison of 2 DateTime objects
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator<=(const DateTime& d1, const DateTime& d2)
{
    return !(d1 > d2);
}

///@brief Overload of >= operator : comparison of 2 DateTime objects
///@param d1 Left hand operand
///@param d2 Right hand operand
bool operator>=(const DateTime& d1, const DateTime& d2)
{
    return !(d1 < d2);
}


